#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>

#define DEBUG 0
#define NOISY_TEST 1

int playGame(struct gameState *G, int *kingdomCards, FILE *fp) {
    char name[][20] = {"curse","estate","duchy","province","copper",
                        "silver","gold","adventurer","council room","feast",
                        "gardens","mine","remodel","smithy","village",
                        "baron","great hall","minion","steward","tribute",
                        "ambassador","cutpurse","embargo","outpost",
                        "salvager","sea hag","treasure map"};
    int currPlayer, handPos, card, i, n, k, action, turncount;
    int choice1, choice2, choice3;

    turncount = 0;

    //Tries buying random cards until buys are 0 or coins are <2
    //Tries playing random cards until actions are 0
    while (isGameOver(G) == 0) {
        fflush(fp);
        currPlayer = G->whoseTurn;
        fprintf(fp, "\nTURN %d BEGINS:", turncount);
        fprintf(fp, " Player %d's hand at the start of the turn:\n",
                currPlayer);
        for (i = 0; i < G->handCount[currPlayer]; i++)
            fprintf(fp, "%s ", name[G->hand[currPlayer][i]]);
        fprintf(fp, "\nCoins at the start of the turn:%d\n", G->coins);
		fprintf(fp, "Num. of cards in deck: %d\n", G->deckCount[currPlayer]);
		fprintf(fp, "Num. of cards in discarded: %d\n", G->discardCount[currPlayer]);

        //PLAY PHASE
        fprintf(fp, "Player %d is trying to play a card:\n", currPlayer);
        fflush(fp);

        while (G->numActions > 0) {

            i = 0; n = 0;
            while (i < G->handCount[currPlayer]) {
                card = G->hand[currPlayer][i];
                if (card >= adventurer && card <= treasure_map) {
                    handPos = i;
                    i = G->handCount[currPlayer];
                    n++;
                }
                i++;
            }
            if (n == 0) {
                fprintf(fp, "Couldn't find a card to play.\n");
                fflush(fp);
                goto BUYCARD;
            }

            fprintf(fp, "PLAYING: %s from hand position %d.\n",
                    name[card], handPos);
            fflush(fp);

            switch (card) {
            case feast:
                n = 1;
                while (n > 0) {
                    n++;
                    if (n == 10)
                        goto BUYCARD;
                    k = floor(Random() * treasure_map) + 1;
                    if (G->supplyCount[k] > 0 && getCost(k) <= 5)
                        n = -10;
                }
                fprintf(fp, "Bought card: %s with feast.\n", name[k]);
                fflush(fp);
                choice1 = k;
                choice2 = -1;
                choice3 = -1;
                break;

            case mine:
                n = 0;
				int found = 0;
				while (n < G->handCount[currPlayer]) {
					card = G->hand[currPlayer][n];
					if (card == copper || card == silver || card == gold) {
						k = n; found++;
						if (card == copper)
							action = floor(Random() * 2);
						else
							action = floor(Random() * 3);

						if (action == 0)
							choice2 = copper;
						else if (action == 1)
							choice2 = silver;
						else
							choice2 = gold;
						n = G->handCount[currPlayer];
					}
					n++;
				}
				if (found == 0) {
					fprintf(fp, "Couldn't find a treasure in hand.\n");
					goto BUYCARD;
				}
				fprintf(fp, "Trashed %s from handpos %d to buy %s.\n",
				        name[card], k, name[choice2]);
				choice1 = k;
				choice3 = -1;
				break;
				
			case remodel:
				if (G->handCount[currPlayer] < 2) {
					fprintf(fp, "Not enough cards in hand to play it.\n");
					goto BUYCARD;
				}
				choice1 = floor(Random() * G->handCount[currPlayer]);
				int cost = getCost(G->hand[currPlayer][choice1]) + 2;
				n = 1;
				while (n > 0) {
					n++;
					if (n == 10)
						goto BUYCARD;
					choice2 = floor(Random() * treasure_map) + 1;
					if (G->supplyCount[choice2] > 0 &&
					    getCost(choice2) <= cost) {
						n = -10;
					}
				}
				fprintf(fp, "Trashed %s from handpos %d to buy %s.\n",
				        name[G->hand[currPlayer][choice1]],
				        choice1, name[choice2]);
				choice3 = -1;
				break;
				
			case baron:
				choice1 = 0;
				for (n = 0; n < G->handCount[currPlayer]; n++) {
					if (G->hand[currPlayer][n] == estate) {
						k = floor(Random() * 2);
						choice1 = k;
					}
				}
				if (choice1 > 0)
					fprintf(fp, "Decided to trash an estate card.\n");
				else 
					fprintf(fp, "Did not trash an estate card.\n");
				choice2 = -1;
				choice3 = -1;
				break;
				
			case minion:
				choice1 = floor(Random() * 2) + 1;
				if (choice1 == 1)
					fprintf(fp, "Decided to get +2 coins.\n");
				else
					fprintf(fp, "Decided to discard hand.\n");
				choice2 = -1;
				choice3 = -1;
				break;
				
			case steward:
				if (G->handCount[currPlayer] < 3)
					n = floor(Random() * 2) + 1;
				else
					n = floor(Random() * 3) + 1;
				choice1 = n;
				choice2 = -1;
				choice3 = -1;
				if (choice1 == 3) {
					fprintf(fp, "Decided to trash two cards.\n");
					choice2 = handPos;
					choice3 = handPos;
					while (choice2 == handPos)
						choice2 = floor(Random()*G->handCount[currPlayer]);
					while (choice3 == handPos || choice3 == choice2)
						choice3 = floor(Random()*G->handCount[currPlayer]);
					fprintf(fp, "Trashed: %s, %s from handpos: %d, %d.\n",
					        name[G->hand[currPlayer][choice2]],
					        name[G->hand[currPlayer][choice3]],
					        choice2, choice3);
				}
				else if (choice1 == 2)
					fprintf(fp, "Decided to get +2 coins.\n");
				else
					fprintf(fp, "Decided to draw two cards.\n");
				break;
				
			case ambassador:
				if (G->handCount[currPlayer] < 2) {
					fprintf(fp, "Not enough cards to play Ambassador.\n");
					goto BUYCARD;
				}
				choice1 = handPos;
				n = 0;
				while (choice1 == handPos) {
					choice1 = floor(Random() * G->handCount[currPlayer]);
					n++;
					if (n == 10)
						goto BUYCARD;
				}
				
				card = G->hand[currPlayer][choice1];
				k = 0;
				for(n = 0; n < G->handCount[currPlayer]; n++) {
					if (G->hand[currPlayer][n] == card &&
					    n != choice1)
						k = 1;
				}
				choice2 = floor(Random() * (2 + k));
				fprintf(fp, "Reveal: %s from handpos %d; trash %d copies.\n",
				        name[G->hand[currPlayer][choice1]],
				        choice1, choice2);
				break;
				
			case embargo:
				choice1 = floor(Random() * treasure_map) + 1;
				choice2 = -1;
				choice3 = -1;
				fprintf(fp, "Placed an embargo on %s cards.\n",
				        name[choice1]);
				break;
				
			case salvager:
				if (G->handCount[currPlayer] < 2) {
					fprintf(fp,"Not enough cards in hand for salvager.\n");
					goto BUYCARD;
				}
				choice1 = handPos;
				n = 0;
				while (choice1 == handPos) {
					choice1 = floor(Random() * G->handCount[currPlayer]);
					if (n == 10)
						goto BUYCARD;
				}
				choice2 = -1;
				choice3 = -1;
				fprintf(fp, "Trashed %s from handpos %d.\n",
				        name[G->hand[currPlayer][choice1]], choice1);
				break;
				
			default:
				choice1 = -1;
				choice2 = -1;
				choice3 = -1;
			}
			if( playCard(handPos, choice1, choice2, choice3, G) < 0) {
                fprintf(fp, "Something went wrong while playing the"
                             " card !!!\n");
            }
		}

BUYCARD:
		//BUY PHASE
		while (G->numBuys > 0) {
			fflush(fp);

			//Randomly buys either a victory, treasure, or action card
			fprintf(fp, "Player %d is trying to buy a card:\n", currPlayer);
			action = floor(Random() * 5);
		    
			//Buying a victory card
			if (action == 0) {
				k = 10;
				fprintf(fp, "Decided to buy a victory card.\n");
				fflush(fp);
				if (G->coins >= 8 && G->supplyCount[province] > 0) {
					card = province;
					k = buyCard(card, G);
				}
				else if (G->coins >= 5 && G->supplyCount[duchy] > 0) {
					card = duchy;
					k = buyCard(card, G);
				}
				else if (G->supplyCount[estate] > 0) {
					card = estate;
					k = buyCard(card, G);
				}
				
				if (k == 10)
					fprintf(fp, "Decided not to buy card.\n");
				else if (k == 0)
					fprintf(fp, "Player %d successfully bought a: %s\n",
					        currPlayer, name[card]);
				else
					fprintf(fp, "!Failed to buy %s!\n", name[card]);
			}
			
			//Buying a treasure card
			if (action == 1) {
				k = 10;
				fprintf(fp, "Decided to buy a treasure card.\n");
				fflush(fp);
				if (G->coins >= 6 &&  G->supplyCount[gold] > 0) {
					card = gold;
					k = buyCard(card, G);
				}
				else if (G->coins >= 3 && G->supplyCount[silver] > 0) {
					card = silver;
					k = buyCard(card, G);
				}
				else if (G->supplyCount[copper] > 0) {
					card = copper;
					k = buyCard(card, G);
				}
				
				if (k == 10)
					fprintf(fp, "Decided not to buy card.\n");
				else if (k == 0)
					fprintf(fp, "Player %d successfully bought a: %s\n",
					        currPlayer, name[card]);
				else
					fprintf(fp, "Failed to buy %s!\n", name[card]);
			}

			//Buying an action card
			if (action == 2) {
				k = 10;
				fprintf(fp, "Decided to buy an action card.\n");
				fflush(fp);
				
				if (G->coins < 2) {
					fprintf(fp, "Not enough coins!\n");
					goto ENDTURN;
				}
				else if (G->coins < 3) {
					if (G->supplyCount[embargo] > 0) {
						card = embargo;
						k = buyCard(card, G);
					}
					else {
						fprintf(fp, "Not enough coins!\n");
						goto ENDTURN;
					}
				}
				else {
					n = 1;
					while (n > 0) {
						n++;
						if (n == 15)
							goto ENDTURN;
						card = floor(Random() * 20) + adventurer;
						if (getCost(card) <= G->coins &&
						    G->supplyCount[card] > 0) {
							k = buyCard(card, G);
							n = -10;
						}
					}
				}

				if (k == 10)
					fprintf(fp, "Decided not to buy card.\n");
				else if (k == 0)
					fprintf(fp, "Player %d successfully bought a: %s\n",
					        currPlayer, name[card]);
				else
					fprintf(fp, "Failed to buy a card!\n");
				
			}
		}

ENDTURN:
		//END TURN
		fflush(fp);
		fprintf(fp, "Player %d is ending his turn.\n", currPlayer);
		fprintf(fp, "Hand at the end of player %d's turn:\n", currPlayer);
		for (i = 0; i < G->handCount[currPlayer]; i++)
			fprintf(fp, "%s ", name[G->hand[currPlayer][i]]);
		fprintf(fp, "\nCoins at the end of the turn:%d\n", G->coins);
		fprintf(fp, "Num. of cards in deck: %d\n", G->deckCount[currPlayer]);
		fprintf(fp, "Num. of cards in discarded: %d\n", G->discardCount[currPlayer]);
		fflush(fp);
		endTurn(G);
		turncount++;
		if (turncount > 1000) {
			fprintf(fp, "TERMINATING AT 1000 TURNS\n");
			fflush(fp);
			goto ENDGAME;
		}
	}

ENDGAME:
	fprintf(fp, "FINISHED GAME!\n");
	fprintf(fp, "End scores:\n");
	for (i = 0; i < G->numPlayers; i++)
		fprintf(fp, "Player %d: %d\n", i, scoreFor(i, G));
	fflush(fp);
	return 0;
}

int main() {
	char name[][20] = {"curse","estate","duchy","province","copper",
	                    "silver","gold","adventurer","council room","feast",
	                    "gardens","mine","remodel","smithy","village",
	                    "baron","great hall","minion","steward","tribute",
	                    "ambassador","cutpurse","embargo","outpost",
	                    "salvager","sea hag","treasure map"};
	SelectStream(2);
	PutSeed(30); // default is 6
	
	FILE *fp = fopen("gameResults.out", "w");
	if (fp == NULL) {
		printf("Error opening output file");
		exit(0);
	}
	
	fprintf(fp, "%s", "RANDOM TEST BEGINS:\n");
	
	struct gameState G;
	int numplayers = floor(Random() * 3) + 2;
	
	fprintf(fp, "Number of players: %d\n", numplayers);

	int i;
	int kingdomCards[10];
	int done = 0; int k = 0;
	
	for (i = 0; i < 10; i++)
		kingdomCards[i] = -1;
	
	//Generate kingdom cards
	while (done == 0) {
		int dup = 0;
		int card = floor(Random() * 20) + adventurer;
		for (i = 0; i < 10; i++) {
			if (kingdomCards[i] == card)
				dup = 1;
		}
		if (dup == 0)
			kingdomCards[k++] = card;
		if (k == 10)
			done++;
	}
	fprintf(fp, "Kingdom cards: ");
	for (i = 0; i < 10; i++)
		fprintf(fp, "%s ", name[kingdomCards[i]]);
	fprintf(fp, "\n");
	fflush(fp);
	k = initializeGame(numplayers, kingdomCards, 1, &G);
	assert(k != -1);

	playGame(&G, kingdomCards, fp);

	fclose(fp);
	return 0;
}
